<template>
  <div>
    <!-- <h1>属性管理</h1> -->
    <!-- <el-card style="margin: 0px 10px;"> -->
    <!-- 注：“inline”是“布尔属性”，也可以把代码写作“:inline="true"”↓ -->
    <!-- <el-card style="margin: 0px 10px 10px 10px;">
        <el-form inline>
          <el-form-item label="一级分类">
            <el-select>
              <el-option label="北京"></el-option>
              <el-option label="上海"></el-option>
              <el-option label="广州"></el-option>
              <el-option label="深圳"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="二级分类">
            <el-select>
              <el-option label="北京"></el-option>
              <el-option label="上海"></el-option>
              <el-option label="广州"></el-option>
              <el-option label="深圳"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="三级分类">
            <el-select>
              <el-option label="北京"></el-option>
              <el-option label="上海"></el-option>
              <el-option label="广州"></el-option>
              <el-option label="深圳"></el-option>
            </el-select>
          </el-form-item>
        </el-form>
      </el-card> -->
    <!-- 使用三级分类全局组件（行内表单）↓ -->
    <Category
      :aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown="aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown" />
    <el-card style="margin: 0px 10px;">
      <div
        v-show="aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown == 'aDivElementIncludeTable'">
        <!-- 注：如果已经获取到了三级分类的ID，那么可以添加商品属性，不禁用“添加属性”按钮，否则则禁用该按钮↓ -->
        <el-button type="primary" size="default" icon="Plus" :disabled="categoryStore.idOfCategory3 ? false : true"
          @click="handlerForUserClickTheAddAttributeButton" v-haveThisButton="`btn.Attr.add`">添加属性</el-button>
        <el-table border style="margin: 10px 0px;" :data="anArrayThatStoreAttributeAndItsValue">
          <el-table-column label="序号" type="index" align="center" width="80px"></el-table-column>
          <el-table-column label="属性名称" width="120px" align="center" prop="attrName"></el-table-column>
          <el-table-column label="属性值名称" align="center">
            <template #default="{ row, $index }">
              <el-tag style="margin: 5px;" v-for="(item, index) in row.attrValueList" :key="item.id">
                {{ item.valueName }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="120px" align="center">
            <!-- 注：“row”表示“与表格中的鼠标当前所在行对应的数据对象”（在此处是表示一个商品属性的对象）↓ -->
            <template #default="{ row, $index }">
              <el-button icon="Edit" type="primary" size="small" v-haveThisButton="`btn.Attr.update`"
                @click="aFunctionUsedToUpdateAttribute(row)"></el-button>
              <!-- <el-button icon="Delete" type="danger" size="small" @click="handlerForUserClickTheDeleteAttributeButton(row.id, $index)"></el-button> -->
              <el-popconfirm :title="`你确定要删除属性“${row.attrName}”吗？`" width="200px" @confirm="handlerForUserClickTheDeleteAttributeButton(row.id)">
                <template #reference>
                  <el-button icon="Delete" type="danger" size="small" v-haveThisButton="`btn.Attr.remove`"></el-button>
                </template>
              </el-popconfirm>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <!-- <div v-show="aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown == 'anotherDivElement'">测试文字</div> -->
      <div v-show="aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown == 'anotherDivElement'">
        <!-- 这里是展示“添加属性”与“修改属性”的页面结构↓ -->
        <el-form inline class="inlineForm">
          <el-form-item label="属性名称">
            <el-input placeholder="请您输入属性名称"
              v-model="anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrName"></el-input>
          </el-form-item>
          <!-- 注：由于不能添加“空属性”，所以要使得“添加属性”按钮在“属性名有值”时才可被点击，否则则添加“禁用”效果↓ -->
          <el-button type="primary" size="default" icon="Plus"
            :disabled="anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrName ? false : true"
            @click="handlerForUserClickTheAddAttributeValueButton">添加属性值</el-button>
          <!-- <el-button type="primary" size="default">确定</el-button> -->
          <el-button size="default" @click="handlerForUserClickTheCancelButton">取消</el-button>
        </el-form>
        <el-table border style="margin: 10px 0px;"
          :data="anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList">
          <el-table-column label="序号" align="center" width="80px" type="index"></el-table-column>
          <el-table-column label="属性值名称" align="center">
            <!-- 注：此处的“row”就是与当前鼠标光标所在行对应的“属性值对象”↓ -->
            <template #default="{ row, $index }">
              <!-- <el-input v-model="row.valueName" size="small" @blur="aFunctionBeUsedToChangeTheElInputComponentToPreviewMode" placeholder="请输入属性值的名称" v-if="aVariableUsedToControlTheElInputComponentShouldBeShown"></el-input> -->
              <!-- <el-input v-model="row.valueName" size="small" @blur="aFunctionBeUsedToChangeTheElInputComponentToPreviewMode(row,$index)" placeholder="请输入属性值的名称" v-if="row.flag"></el-input> -->
              <!-- <el-input v-model="row.valueName" :ref="handlerForGetTheInstanceOfElInputComponent" size="small" @blur="aFunctionBeUsedToChangeTheElInputComponentToPreviewMode(row,$index)" placeholder="请输入属性值的名称" v-if="row.flag"></el-input> -->
              <el-input v-model="row.valueName"
                :ref="(TheInstanceOfElInputComponent: any) => anArrayThatStoreTheInstanceOfElInputComponent[$index] = TheInstanceOfElInputComponent"
                size="small" @blur="aFunctionBeUsedToChangeTheElInputComponentToPreviewMode(row, $index)"
                placeholder="请输入属性值的名称" v-if="row.flag"></el-input>
              <!-- 在此处放置一个“div”元素（这个元素被用于在文本输入框失去焦点时显示被输入的内容）↓ -->
              <!-- <div v-else @click="aFunctionBeUsedToChangeTheElInputComponentToEditMode">{{ row.valueName }}</div> -->
              <!-- 在此处传入数组的下标，以在用户点击显示内容的div元素时，与数组索引对应的（即鼠标光标当前所在行的）文本输入框自动完成聚焦↓ -->
              <div v-else @click="aFunctionBeUsedToChangeTheElInputComponentToEditMode(row, $index)">{{ row.valueName }}
              </div>
            </template>
          </el-table-column>
          <el-table-column label="操作" align="center">
            <template #default="{ row, $index }">
              <!-- 删除按钮↓ -->
              <el-button type="danger" size="small" icon="Delete"
                @click="anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList.splice($index, 1)"></el-button>
            </template>
          </el-table-column>
        </el-table>
        <!-- 为“保存”按钮添加禁用效果，以确保“保存”按钮在“具备属性值”（即数组“attrValueList”的长度“大于0”）时（进一步改进：且“第一个属性值有名称”时（这样做的好处是在后续发送请求时避免向后台服务器发送“名称为空”的属性值））才可被点击↓ -->
        <el-button type="primary" size="default" @click="handlerForUserClickTheSaveButton"
          :disabled="anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList.length > 0 && anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList[0].valueName !== '' ? false : true">保存</el-button>
        <el-button size="default" @click="handlerForUserClickTheCancelButton">取消</el-button>
      </div>
    </el-card>
  </div>
</template>

<script lang="ts" setup name="Attr">
// 导入并获取涉及“商品分类”的仓库↓
import useCategoryStore from '@/store/modules/category'
import type { typeOfResponseDataWhenGettingAllOfAttributesAndTheirValuesForASpecificCategory3, typeOfObjectThatRepresentsAnAttribute } from '@/api/product/attr'
let categoryStore = useCategoryStore()
// 导入监听器函数“watch”（以监听仓库中是否存在三级分类的ID，如果有，则发送请求获取具体分类项下的属性与属性值，并把它们展示到表格中）↓
import { watch, ref, reactive, nextTick,onBeforeMount } from 'vue'
import { ElMessage } from 'element-plus' // 导入“消息提示”组件
// 导入“在一级——三级分类被全部选中的情况下，向获取某个具体的三级分类项下的所有属性及其属性值时需要用到的接口发送请求时需要用到的方法”↓
import { requestAttrAndItsValue, requestAddOrUpdateAttr, requestDeleteAttr } from '@/api/product/attr'
// 定义一个变量，它被用于控制哪一个div元素应该被展示（如果其原始数据为“aDivElementIncludeTable”，那么显示包含表格的div元素，否则则显示被用于添加与修改属性的页面结构）↓
let aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown = ref("aDivElementIncludeTable")
// 定义一个变量，它被用于控制“文本输入框”是否应该被展示↓
// 注：因为这种方法只适用于控制一个文本输入框的显示与隐藏，而在本项目中，一个属性可能同时拥有多个属性值，在拥有多个属性值时使用这种方法会导致输入所有属性值的文本输入框被同时控制切换显示与隐藏，不太合适，因此这里不使用这种方法，把下面的这一行代码以及使用到变量“aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown”的所有代码全部注释↓
// let aVariableUsedToControlTheElInputComponentShouldBeShown = ref(true)
// let aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown = ref("anotherDivElement")
// 定义一个对象，它被用于存储“正在被添加或修改的属性”的数据↓
/* 注：这里是对在发送“添加属性”和“修改已有的属性”的请求时需要携带的参数的解释：
   1.对“发送修改已有的属性的请求时需要携带的参数”的解释：
   a.id：正在被修改的属性的ID；
   b.attrName：正在被修改的属性的名字；
   c.（在属性值对象中的）attrId：正在被修改的属性的ID（它被用于表示正在被修改的这个属性值从属于哪一个属性）；
   d.（在属性值对象中的）id：正在被修改的这个属性值的ID；
   e.（在属性值对象中的）valueName：正在被修改的这个属性值的名字；
   f.categoryId：这个属性的所在分类的ID（它被用于表示正在被修改的这个属性值从属于哪一个分类）；
   g.categoryLevel：这个属性的所在分类的级别；
   2.对“发送添加属性的请求时需要携带的参数”的解释：
   a.attrName：新增的属性的名字；
   b.attrValueList：新增的属性值列表（它是一个数组）
   c.valueName：新增的属性值的名字；
   d.categoryId：新增的属性的所在分类的ID（它被用于表示新增的这个属性从属于哪一个分类）；
   e.categoryLevel：新增的这个属性的所在分类的级别（注：其值一定是3） */
let anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated = reactive<typeOfObjectThatRepresentsAnAttribute>({
  attrName: '',
  attrValueList: [],
  categoryId: "",
  categoryLevel: 3,
})
// 定义一个数组，它被用于存储文本输入框的实例↓
let anArrayThatStoreTheInstanceOfElInputComponent = ref<any>([])
// watch(() => categoryStore.idOfCategory3,async () => {
watch(() => categoryStore.idOfCategory3, () => {
  // 注：如果这三个级别的分类中的某一个级别的分类发生了变化，那么需要清空上次获取分类时保存的数据↓
  anArrayThatStoreAttributeAndItsValue.value = [] // 清空上次获取分类时保存的数据
  // 解构赋值，获取仓库中存储的一级、二级和三级分类的ID↓
  /* const {idOfCategory1,idOfCategory2,idOfCategory3} = categoryStore
  console.log('一级分类ID：', idOfCategory1) // 一级分类ID： 1
  console.log('二级分类ID：', idOfCategory2) // 二级分类ID： 2
  console.log('三级分类ID：', idOfCategory3) // 三级分类ID： 5 */
  // let result = await requestAttrAndItsValue(idOfCategory1,idOfCategory2,idOfCategory3) // 发送请求，获取某个具体的三级分类项下的所有属性及其属性值
  if (!categoryStore.idOfCategory3) {
    return; // 如果三级分类的ID不存在，则直接返回，退出函数执行（以避免用户在切换一级分类后不携带ID，发送无效的请求）
  }
  else { getAttributeAndItsValue() }
})
// 定义一个变量（它是一个数组），它被用于存储某个具体的三级分类项下的所有属性及其属性值↓
let anArrayThatStoreAttributeAndItsValue = ref<typeOfObjectThatRepresentsAnAttribute[]>([])
/**
 * 函数“getAttributeAndItsValue”被用于“获取某个具体的三级分类项下的所有属性及其属性值”
 */
async function getAttributeAndItsValue() {
  const { idOfCategory1, idOfCategory2, idOfCategory3 } = categoryStore
  console.log('一级分类ID：', idOfCategory1)
  console.log('二级分类ID：', idOfCategory2)
  console.log('三级分类ID：', idOfCategory3)
  let result: typeOfResponseDataWhenGettingAllOfAttributesAndTheirValuesForASpecificCategory3 = await requestAttrAndItsValue(idOfCategory1, idOfCategory2, idOfCategory3)
  console.log(result) // Object
  if (result.code == 200) {
    anArrayThatStoreAttributeAndItsValue.value = result.data // 把获取到的属性及其值存储到数组中
  }
}
/**
 * 函数“handlerForUserClickTheAddAttributeButton”被用于处理用户点击添加属性按钮时发生的事件
 */
function handlerForUserClickTheAddAttributeButton() {
  // 每次点击“添加属性”按钮后，在存储三级分类的ID前，先清空上次添加属性时保存的数据（通过使用“Object.assign”方法合并对象，来完成这个操作）↓
  Object.assign(anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated, {
    attrName: '',
    attrValueList: [],
    // categoryId: "",
    categoryId: categoryStore.idOfCategory3, // 在合并对象时，顺便把三级分类的ID也合并进来
    categoryLevel: 3,
  })
  // 切换变量，以显示另一个div元素（即显示“添加属性”与“修改属性”的页面结构）↓
  aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown.value = "anotherDivElement"
  // anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.categoryId = categoryStore.idOfCategory3 // 把三级分类的ID保存到对象中
}
/**
 * 函数“aFunctionUsedToUpdateAttribute”被用于处理用户点击修改属性按钮时发生的事件
 */
function aFunctionUsedToUpdateAttribute(row: typeOfObjectThatRepresentsAnAttribute) {
  aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown.value = "anotherDivElement"
  // anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated = row // 把与当前被用户点击的“更新（修改）属性”按钮所在行对应的属性对象赋值给变量“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”
  // console.log(anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated) // Proxy(Object) {id: 283, createTime: null, updateTime: null, attrName: '12222222', categoryId: 6, …}
  // 通过ES6提供的“Object.assign”方法来合并对象，把对象“row”中的数据合并到变量“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”存储的对象中↓
  // 注：这样做后有“坑”↑↓（这个“坑”就是“用户修改属性值后如果不点击‘保存按钮’，而是点击‘取消按钮’，那么切换回显示当前分类下的属性及与之对应的属性值的表格后，属性值仍会显示‘未被保存的数据’”）（老师说这种现象与“深拷贝”和“浅拷贝”（这是面试中的常考题）有关）。以下内容是对出现这个坑的原因的解释：
  // 因为“Object.assign”方法是一种“浅拷贝”方法，这样做后使得“row”和“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”指向了同一个内存地址，因此当其中一个对象中的数据被修改时，另一个对象中对应的数据也会随之改变。为了解决这个问题，可以使用“深拷贝”的方法↓
  // Object.assign(anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated,row)
  Object.assign(anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated, JSON.parse(JSON.stringify(row))) // 通过“深拷贝”的方法，把对象“row”中的数据合并到变量“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”存储的对象中（这样做后，变量“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”和对象“row”将指向不同的内存地址）
  /* 注：文心快码解释了“在此处，为什么结合使用‘Object.assign()’、‘JSON.parse()’和‘JSON.stringify()’可以实现深拷贝”：
  JSON.stringify(row) 会将 row 转换为一个 JSON 字符串，然后 JSON.parse() 会将这个字符串转换回一个全新的对象。由于这个过程涉及字符串的转换，所以它会递归地复制对象中的所有嵌套属性，从而实现深拷贝。
  在用户提供的Vue组件代码中，当处理用户点击修改属性按钮时，为了避免浅拷贝带来的问题（即修改新对象中的属性会影响到原对象），使用了 JSON.parse(JSON.stringify(row)) 来实现深拷贝。
  这样，即使后续修改了 anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated 对象中的属性，也不会影响到原始的 row 对象，从而保证了数据的独立性。 */
}
/**
 * 函数“handlerForUserClickTheCancelButton”被用于处理用户点击取消按钮时发生的事件
 */
function handlerForUserClickTheCancelButton() {
  // 切换变量，以显示包含表格的div元素↓
  aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown.value = "aDivElementIncludeTable"
}
/**
 * 函数“handlerForUserClickTheAddAttributeValueButton”被用于处理用户点击添加属性值按钮时发生的事件
 * @param event 事件对象
 */
function handlerForUserClickTheAddAttributeValueButton(event: Event) {
  // alert("您点击了添加属性值按钮！")
  // 在用户点击“添加属性值”按钮时，向数组中添加一个新的对象↓
  anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList.push({
    valueName: '',
    // 注：由于“{} === {}”将返回“false”（因为不同的对象指向了不同的内存地址），所以可以在每次点击“添加属性值”按钮时，为对象“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”添加“flag”属性，通过这个属性的值是真还是假来控制每一个属性值输入框是否应该被展示↓
    flag: true,
  })
  // 注：下面的代码被用于“在用户点击添加属性值按钮时，让最后一个文本输入框获得焦点”↓
  nextTick(() => {
    anArrayThatStoreTheInstanceOfElInputComponent.value[anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList.length - 1].focus() // 通过调用实例对象的“focus”方法，来让文本输入框获得焦点
  })
}
/**
 * 函数“handlerForUserClickTheSaveButton”被用于处理用户点击保存按钮时发生的事件
 * @param event 事件对象
 */
async function handlerForUserClickTheSaveButton(event: Event) {
  // 注：开发者在这个函数中编写的代码将做两件事情（但此处只做了第二件事）：
  // 1.把已获取到的某个具体的三级分类的ID保存到对象“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”中（这件事情也可以在函数“handlerForUserClickTheAddAttributeButton”的代码中完成（因为在本项目中，“添加属性”按钮必须在用户点击某个具体的三级分类，获取到这个分类的ID后，才可被点击））；
  // 2.把对象“anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated”中的数据发送到后端，以实现添加或修改属性的功能。↓
  let result: any = await requestAddOrUpdateAttr(anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated)
  console.log(result) // {code: 200, message: '成功', data: null, ok: true}
  if (result.code == 200) {
    // 如果进入了这个代码块，那么说明添加或修改属性成功，此时需要重新获取该三级分类下的所有属性及其值↓
    aVariableUsedToControlWhichDivElementUnderTheElCardComponentShouldBeShown.value = "aDivElementIncludeTable" // 切换变量，以显示包含表格的div元素
    // 显示提示信息↓
    ElMessage({
      type: 'success',
      message: anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.categoryId ? '修改属性成功' : '添加属性成功',
    })
    getAttributeAndItsValue() // 注：添加或修改属性成功后，需要重新获取该三级分类下的所有属性及其值
  } else {
    ElMessage({
      type: 'error',
      message: anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.categoryId ? '修改属性失败' : '添加属性失败',
    })
  }
}
/**
 * 函数“aFunctionBeUsedToChangeTheElInputComponentToPreviewMode”被用于把文本输入框变为预览模式（即把文本输入框变为只读模式）

 */
function aFunctionBeUsedToChangeTheElInputComponentToPreviewMode(row: typeOfObjectThatRepresentsAnAttribute, index: number) {
  // aVariableUsedToControlTheElInputComponentShouldBeShown.value = false // 切换变量，以让文本输入框变为预览模式
  // 注：下面的代码被用于“防范用户的非法操作”↓
  // 情况1：防止用户不输入属性值名称后就直接让文本输入框失去焦点，造成出现“div元素的高度为0，无法被点击”的BUG↓
  if (row.valueName.trim() == "") { // 通过这个条件判断语句来确保只有当文本输入框中的内容有值时，才可以让文本输入框变为预览模式↓
    anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList.splice(index, 1) // 如果文本输入框中的内容（即“属性值名称”）为空，则从“存储了属性值对象的数组”中删除这个属性值对象（因为在后续发送请求时，向后台服务器发送名称为空的属性值无意义）
    ElMessage({
      type: 'error',
      message: '属性值名称不能为空'
    })
    return; // 如果文本输入框中的内容为空，则直接返回，退出函数执行（以避免出现用户不输入属性值名称时就直接让文本输入框失去焦点，造成div元素的高度为0，无法被点击的BUG）
  }
  // 情况2：防止用户输入的属性值名称与其它属性值对象的“属性值名称”出现重复↓
  // 通过数组提供的“find”方法，查找数组中是否存在一个除了当前正在操作的属性值对象以外的其它属性值对象的“属性值名称”与当前文本输入框中的内容相同↓
  let isThisObjectInTheAttrValueListBeRepeated = anObjectUsedToStoreTheDataOfAnAttributeThatBeAddedOrUpdated.attrValueList.find((item: typeOfObjectThatRepresentsAnAttribute) => {
    if (item !== row) { // 注：通过这个判断条件来把当前正在操作的属性值对象排除在外（之所以需要这样做，是因为如果让当前正在操作的属性值对象也参与查找，那么find方法将总会返回当前正在操作的属性值对象，导致find方法无法实现预期的功能）
      return item.valueName == row.valueName // 如果找到了一个属性值对象的“属性值名称”与当前文本输入框中的内容相同，则返回这个对象
    }
  })
  console.log(isThisObjectInTheAttrValueListBeRepeated) // 如果找到了一个属性值对象的“属性值名称”与当前文本输入框中的内容相同，则控制台中将输出这个对象的代理对象（例如“Proxy(Object) {valueName: '1', flag: false}”）；否则，控制台中将输出“undefined”
  if (isThisObjectInTheAttrValueListBeRepeated) {
    ElMessage({
      type: 'error',
      message: '属性值名称不能重复'
    })
    return; // 如果找到了一个属性值对象的“属性值名称”与当前文本输入框中的内容相同，则直接返回，退出函数执行
  }
  row.flag = false // 通过把属性值对象的“flag”属性修改为“false”，来让文本输入框变为预览模式
}
/**
 * 函数“aFunctionBeUsedToChangeTheElInputComponentToEditMode”被用于把文本输入框变为编辑模式（即把文本输入框变为可编辑状态）
 */
function aFunctionBeUsedToChangeTheElInputComponentToEditMode(row: typeOfObjectThatRepresentsAnAttribute, index: number) {
  // aVariableUsedToControlTheElInputComponentShouldBeShown.value = true // 切换变量，以让文本输入框变为编辑模式
  row.flag = true // 通过把属性值对象的“flag”属性修改为“true”，来让文本输入框变为编辑模式
  // 注：浏览器执行下面的这一行代码时，控制台中输出“null”的原因是：“flag”属性的值刚刚被修改为“true”↑，文本输入框刚刚得以被显示出来，但是渲染页面需要耗时，所以此时文本输入框还没有被渲染到页面上。↓
  // console.log(anArrayThatStoreTheInstanceOfElInputComponent.value[index]) // null
  // 通过使用“nextTick”函数，来确保在文本输入框被渲染到页面上之后，再执行位于回调函数中的代码（nextTick函数被用于在响应式数据发生变化后获取更新后的DOM元素或组件实例）↓
  /* 注：这一段内容是“文心快码”对“nextTick 函数的作用”的解释：
   1. 引言：在 Vue.js 中，nextTick 是一个非常重要的函数，它用于延迟执行代码直到下一个 DOM 更新循环结束之后。在理解 nextTick 的作用之前，我们需要先了解 Vue 的异步更新队列和 DOM 更新机制。
   2. Vue 的异步更新队列：Vue 在修改数据后，不会立即更新 DOM，而是将这些修改放入一个异步更新队列中。只有当队列清空时，Vue 才会进行 DOM 更新。这种机制可以提高性能，避免频繁的 DOM 操作。
   3. nextTick 的作用：nextTick 函数的作用是在下一次 DOM 更新循环结束之后执行延迟回调。这意味着，当开发者调用 nextTick 并传入一个回调函数时，这个回调函数会在 Vue 完成 DOM 更新后立即执行。*/
  nextTick(() => {
    console.log(anArrayThatStoreTheInstanceOfElInputComponent.value[index]) // 此时控制台中输出组件的代理对象：Proxy(Object) {input: RefImpl, textarea: RefImpl, ref: ComputedRefImpl, textareaStyle: ComputedRefImpl, autosize: ObjectRefImpl, …}
    anArrayThatStoreTheInstanceOfElInputComponent.value[index].focus() // 通过调用实例对象的“focus”方法，来让文本输入框获得焦点
  })
}
/**
 * 函数“handlerForGetTheInstanceOfElInputComponent”被用于获取el-input组件的实例对象（这个函数在页面结构发生变化时会被调用）
 * @param TheInstanceOfElInputComponent el-input组件的实例对象
 */
/* function handlerForGetTheInstanceOfElInputComponent(TheInstanceOfElInputComponent:any) {
  // console.log(123)
  // console.log(TheInstanceOfElInputComponent) // 控制台中输出组件实例的代理对象：Proxy(Object) {input: RefImpl, textarea: RefImpl, ref: ComputedRefImpl, textareaStyle: ComputedRefImpl, autosize: ObjectRefImpl, …}
  // return anArrayThatStoreTheInstanceOfElInputComponent[index].value = TheInstanceOfElInputComponent
  return anArrayThatStoreTheInstanceOfElInputComponent[$index] = TheInstanceOfElInputComponent
} */
/**
 * 函数“handlerForUserClickTheDeleteAttributeButton”被用于处理用户点击删除属性按钮时发生的事件
 * @param row 与当前鼠标光标所在行对应的属性对象
 * @param index 属性对象在数组中的索引值
 */
async function handlerForUserClickTheDeleteAttributeButton(idOfAnAttribute: number) {
  let result: any = await requestDeleteAttr(idOfAnAttribute) // 发送请求，以删除属性（注：需要携带ID（以告诉服务器删除哪个属性））
  console.log(result) // {code: 200, message: '成功', data: null, ok: true}
  if (result.code == 200) {
    ElMessage({
      type: 'success',
      message: '删除属性成功',
    })
    getAttributeAndItsValue() // 注：删除属性成功后，需要重新获取该三级分类下的所有属性及其值
  } else {
    ElMessage({
      type: 'error',
      message: '删除属性失败',
    })
  }
}
// 在切换路由，组件销毁前重置位于涉及到与分类相关的仓库实例对象中的数据（注：开发者这样做是为了防止用户在刷新页面后，会看到页面上显示的各级分类列表中残留了上次操作后获取到的数据）↓
onBeforeMount(() => {
  categoryStore.$reset()
})
</script>

<style scoped>
.inlineForm {
  display: flex;
  vertical-align: middle;
}
</style>